﻿// Original author contact info: Owen Emlen (owene_1998@yahoo.com)
// Note: other individuals may also have contributed to this code
// Project hosted on CodePlex.com as of 1/10/2009 at http://www.codeplex.com/EmlenMud
#define TRACKCONFLICTS
using System.Runtime.Serialization;
using System;
using System.Xml.Serialization;
using System.ComponentModel;
using System.Threading;
#if USE_HYPER
using Hyper.ComponentModel;
#endif

namespace BrainTechLLC.ThreadSafeObjects
{

#if USE_HYPER
    // Do nothing
#else
    [DataContract]
    public class HyperTypeDescriptionProvider
#if NO_SILVERLIGHT
 : TypeDescriptionProvider
#endif
    {
    }
#endif

    /// <summary>
    /// Basic support for simple spin-lock - for locking tiny sections of code only, avoiding lock()
    /// Uses atomic CompareExchange operation.  NOTE: no deadlock detection, same thread cannot lock an already-locked object    
    /// </summary>
#if NO_SILVERLIGHT
    [TypeDescriptionProvider(typeof(HyperTypeDescriptionProvider))]
#endif
    [Serializable]
    [DataContract]
    public class Lockable : ILockable
    {
        [NonSerialized, XmlIgnore]
        private readonly static int SpinCycles = 20;

        [NonSerialized, XmlIgnore]
        protected static long _conflicts;

        [NonSerialized, XmlIgnore]
        protected int _lock;

        /// <summary>
        /// Returns the number of lock conflicts that have occurred
        /// </summary>
        public static long Conflicts { get { return _conflicts; } }

        /// <summary>
        /// Aquire the lock
        /// </summary>
        public void AquireLock()
        {
            // Assume that we will grab the lock - call CompareExchange
            if (Interlocked.CompareExchange(ref _lock, 1, 0) == 1)
            {
                int n = 0;

                // Could not grab the lock - spin/wait until the lock looks obtainable
                while (_lock == 1)
                {
                    if (n++ > SpinCycles)
                    {
#if TRACKCONFLICTS
                        Interlocked.Increment(ref _conflicts);
#endif
                        n = 0;
                        Thread.Sleep(0);
                    }
                }

                // Try to grab the lock - call CompareExchange
                while (Interlocked.CompareExchange(ref _lock, 1, 0) == 1)
                {
                    n = 0;

                    // Someone else grabbed the lock.  Continue to spin/wait until the lock looks obtainable
                    while (_lock == 1)
                    {
                        if (n++ > SpinCycles)
                        {
#if TRACKCONFLICTS
                            Interlocked.Increment(ref _conflicts);
#endif
                            n = 0;
                            Thread.Sleep(0);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Release the lock
        /// </summary>
        public void ReleaseLock()
        {
            _lock = 0;
        }

        /// <summary>
        /// Is the lock currently locked?
        /// </summary>
        public bool Locked { get { return (_lock != 0); } }

        /// <summary>
        /// Attempts lock aquisition, returns true if lock aquired, otherwise false
        /// </summary>
        /// <returns></returns>
        public bool TryAquireLock()
        {
            if (_lock == 1) return false;
            return (Interlocked.CompareExchange(ref _lock, 1, 0) != 1);
        }
    }
}